/*
    James McQueen, Tyson Nottingham, Michael Pogson
    TCSS 372 Winter 2012
    SC2 Simulator
    
    cpu.h
*/

#ifndef CPU_H
#define CPU_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sc2sim.h"


/***************** MACROSTATES ****************/
#define FETCH 100
#define DECODE 200
#define FETCH_OPERANDS 300
#define EXECUTE 400
#define WRITE 500
#define STOP 0


/******************* OPCODES ******************/
/* Data Movement */
#define LDI 0x1
#define LDA 0x2
#define LDB 0x3
#define LDW 0x4
#define STB 0x5
#define STW 0x6
#define PUSH_OR_POP 0x7

/* ALU */
#define ALU_OPCODE 0x9
#define SHL_OPCODE 0x8
#define SHR_OPCODE 0xA

/* Control */
#define BRI 0x10
#define BR 0x11
#define BRIX 0x12
#define BRX 0x13
#define JSR 0x14
#define JSRX 0x15
#define TRAP 0x16
#define RET_OR_RETI 0x17

/* I/O */
#define IN_OR_OUT 0x18

/* Miscellaneous */
#define EI_OR_DI 0x19
#define HALT 0x1A
#define NOP 0x1B
#define LDOS 0x1C
#define LDRO 0x1D


/************** ADDRESSING MODES **************/
#define REGISTER 0
#define BASE_RELATIVE 1
#define INDEX 2
#define INDIRECT 3
#define REGISTER_MOVE 4


/*************** PUSH/POP MODES ***************/
#define PUSHB 0
#define POPB 1
#define PUSHW 2
#define POPW 3


/*************** ALU OPERATIONS ***************/
#define ADD 0
#define SUB 1
#define MUL 2
#define DIV 3
#define AND 4
#define OR 5
#define XOR 6
#define NOT 7
#define SHL 8
#define SHR 9
/* Special handling needed for SHL/SHR since opcodes are different. */


/********** BRANCH/JUMP CONDITIONALS **********/
#define ON_NEGATIVE 0
#define ON_ZERO 1
#define ON_CARRY 2
#define ON_OVERFLOW 3


/**************** RETURN MODE *****************/
#define RET 0
#define RETI 1


/****************** I/O MODES *****************/
#define IN_MODE 0
#define OUT_MODE 1


/****************** I/O PORTS *****************/
#define IO_PORTS 64
#define PIT0_COUNTER 0x00
#define PIT0_STATUS 0x01
#define PIT0_CTRL 0x02
#define ADC_CTRL 0x08
#define ADC_STATUS 0x09
#define ADC_DATA 0x0A
#define ADC_CHANNEL 0x0B
#define PIT1_COUNTER 0x10
#define PIT1_STATUS 0x11
#define PIT1_CTRL 0x12
#define SIO1 0x18
#define SIO2 0x20
#define PIO0_STATUS 0x28
#define PIO0_CTRL 0x29
#define PIO0_DATA 0x2A
#define VID_STATUS 0x30
#define VID_CTRL 0x31
#define VID_DATA 0x32
#define KBD_STATUS 0x38
#define KBD_CTRL 0x39
#define KBD_DATA0 0x3A
#define KBD_DATA1 0x3B


/******************** IVT *********************/
#define IVT 0x2100
#define PIT0_INTV 0x00
#define ADC_INTV 0x02
#define PIT1_INTV 0x04
#define SIO1_INTV 0x06
#define SIO2_INTV 0x08
#define PIO0_INTV 0x10
#define VID_INTV 0x12
#define KBD_INTV 0x14


/****************** IRQ MODE ******************/
#define ENABLE_MODE 0
#define DISABLE_MODE 1


/********** INSTRUCTION MASKS/SHIFTS **********/
#define OPCODE_MASK 0xF800

#define REG_OP1_MASK 0x0780
#define REG_OP2_MASK 0x0078
#define TRAPV_MASK 0x07E0
#define IO_PORT_MASK 0x007E
#define IRQ_MASK 0x07F8
#define IMMED7_MASK 0x007F
#define IMMED9_MASK 0x01FF
#define IMMED11_MASK 0x07FF

#define ADDR_MODE_MASK 0x0007
#define STACK_MODE_MASK 0x0003
#define ALU_OPERATION_MASK 0x0007
#define BR_MODE_MASK 0x0003
#define RET_MODE_MASK 0x0001
#define IO_MODE_MASK 0x0001
#define IRQ_MODE_MASK 0x0001

#define OPCODE_SHIFT 11
#define REG_OP1_SHIFT 7
#define REG_OP2_SHIFT 3
#define TRAPV_SHIFT 5
#define IO_PORT_SHIFT 1
#define IRQ_SHIFT 3
#define IMMED9_SHIFT 2


/******** INSTRUCTION OPERANDS INDICES ********/
#define OPCODE 0
#define REG_OP1 1
#define REG_OP2 2
#define TRAPV 3
#define IO_PORT 4
#define IRQ 5
#define IMMED9 6
#define IMMED7 7
#define IMMED11 8
#define ADDR_MODE 9
#define STACK_MODE 10
#define ALU_OPERATION 11
#define BR_MODE 12
#define RET_MODE 13
#define IO_MODE 14
#define IRQ_MODE 15

#define NUM_OPERANDS 16


/************** STATUS WORD MASKS *************/
#define N_MASK 0x8000
#define Z_MASK 0x4000
#define C_MASK 0x2000
#define O_MASK 0x1000


/*************** CPU DEFINITION ***************/

typedef struct CPUStr {
    RegisterFilePtr rf;
    Register SW, PC, IR, MDR, MAR, OSB, TXB;
    ALUPtr alu;
    MemoryModulePtr mm;
    ushort operand[NUM_OPERANDS];
    int (*operation)(struct CPUStr*);
    int macroState;
    uchar ioport[IO_PORTS];
} CPU;

typedef CPU* CPUPtr;


/***************** PROTOTYPES *****************/
/* Constructor */
CPUPtr cpu_initialize();

/* Fetch/Decode/Execute */
int cpu_instructionCycle(CPUPtr cpu);
int cpu_fetch(CPUPtr cpu);
int cpu_decode(CPUPtr cpu);
int cpu_setOperands(CPUPtr cpu);
int cpu_decodeLDB(CPUPtr cpu);
int cpu_decodeLDW(CPUPtr cpu);
int cpu_decodeSTB(CPUPtr cpu);
int cpu_decodeSTW(CPUPtr cpu);
int cpu_decodePUSHorPOP(CPUPtr cpu);
int cpu_decodeRETorRETI(CPUPtr cpu);
int cpu_decodeINorOUT(CPUPtr cpu);
int cpu_decodeEIorDI(CPUPtr cpu);
int cpu_execute(CPUPtr cpu);

/* Data Movement */
int cpu_ldi(CPUPtr cpu);
int cpu_lda(CPUPtr cpu);
int cpu_ldbRegister(CPUPtr cpu);
int cpu_ldbBaseRelative(CPUPtr cpu);
int cpu_ldbIndex(CPUPtr cpu);
int cpu_ldbIndirect(CPUPtr cpu);
int cpu_ldwRegister(CPUPtr cpu);
int cpu_ldwBaseRelative(CPUPtr cpu);
int cpu_ldwIndex(CPUPtr cpu);
int cpu_ldwIndirect(CPUPtr cpu);
int cpu_mov(CPUPtr cpu);
int cpu_stbRegister(CPUPtr cpu);
int cpu_stbBaseRelative(CPUPtr cpu);
int cpu_stbIndex(CPUPtr cpu);
int cpu_stbIndirect(CPUPtr cpu);
int cpu_stwRegister(CPUPtr cpu);
int cpu_stwBaseRelative(CPUPtr cpu);
int cpu_stwIndex(CPUPtr cpu);
int cpu_stwIndirect(CPUPtr cpu);
int cpu_pushb(CPUPtr cpu);
int cpu_popb(CPUPtr cpu);
int cpu_pushw(CPUPtr cpu);
int cpu_popw(CPUPtr cpu);

/* ALU */
int cpu_aluOperation(CPUPtr cpu);

/* Control Flow */
int cpu_bri(CPUPtr cpu);
int cpu_br(CPUPtr cpu);
int cpu_brix(CPUPtr cpu);
int cpu_brx(CPUPtr cpu);
int cpu_jsr(CPUPtr cpu);
int cpu_jsrx(CPUPtr cpu);
int cpu_trap(CPUPtr cpu);
int cpu_ret(CPUPtr cpu);
int cpu_reti(CPUPtr cpu);

/* I/O */
int cpu_in(CPUPtr cpu);
int cpu_out(CPUPtr cpu);

/* Miscellaneous */
int cpu_ei(CPUPtr cpu);
int cpu_di(CPUPtr cpu);
int cpu_halt(CPUPtr cpu);
int cpu_ldos(CPUPtr cpu);
int cpu_ldro(CPUPtr cpu);

/* Get/Set */
RegisterFilePtr cpu_getRegisterFile(CPUPtr cpu);
MemoryModulePtr cpu_getMemoryModule(CPUPtr cpu);
int cpu_isVideoWaiting(CPUPtr cpu);
uchar cpu_getVideo(CPUPtr cpu);
ushort cpu_getSW(CPUPtr cpu);
ushort cpu_getN(CPUPtr cpu);
ushort cpu_getZ(CPUPtr cpu);
ushort cpu_getC(CPUPtr cpu);
ushort cpu_getO(CPUPtr cpu);
ushort cpu_getPC(CPUPtr cpu);
ushort cpu_getIR(CPUPtr cpu);
ushort cpu_getMDR(CPUPtr cpu);
ushort cpu_getMAR(CPUPtr cpu);
void cpu_setSW(CPUPtr cpu, ushort sw);
void cpu_setPC(CPUPtr cpu, ushort pc);
void cpu_setIR(CPUPtr cpu, ushort ir);
int cpu_getState(CPUPtr cpu);
void cpu_reset(CPUPtr cpu);

/* Helper */
ushort sext(ushort data, uchar num_bits);

/****TESTS************/

void testLD(CPUPtr cpu);
void testST(CPUPtr cpu);
void testPushPop(CPUPtr cpu);
void testBranch(CPUPtr cpu);
void testJumps(CPUPtr cpu);

#endif
